import { invoke } from '@tauri-apps/api/core';
import { listen } from '@tauri-apps/api/event';

var DataBits;
(function (DataBits) {
    DataBits["Five"] = "Five";
    DataBits["Six"] = "Six";
    DataBits["Seven"] = "Seven";
    DataBits["Eight"] = "Eight";
})(DataBits || (DataBits = {}));
var FlowControl;
(function (FlowControl) {
    FlowControl["None"] = "None";
    FlowControl["Software"] = "Software";
    FlowControl["Hardware"] = "Hardware";
})(FlowControl || (FlowControl = {}));
var Parity;
(function (Parity) {
    Parity["None"] = "None";
    Parity["Odd"] = "Odd";
    Parity["Even"] = "Even";
})(Parity || (Parity = {}));
var StopBits;
(function (StopBits) {
    StopBits["One"] = "One";
    StopBits["Two"] = "Two";
})(StopBits || (StopBits = {}));
var ClearBuffer;
(function (ClearBuffer) {
    ClearBuffer["Input"] = "Input";
    ClearBuffer["Output"] = "Output";
    ClearBuffer["All"] = "All";
})(ClearBuffer || (ClearBuffer = {}));
const available_ports = async () => {
    try {
        const result = await invoke("plugin:serial|available_ports");
        return Promise.resolve(result);
    }
    catch (error) {
        throw Promise.reject(error);
    }
};
class SerialPort {
    constructor(option) {
        this.isOpen = false;
        this.encoding = option.encoding || "utf-8";
        this.option = {
            path: option.path,
            baudRate: option.baudRate,
            dataBits: option.dataBits || DataBits.Eight,
            flowControl: option.flowControl || FlowControl.None,
            parity: option.parity || Parity.None,
            stopBits: option.stopBits || StopBits.One,
            timeout: option.timeout || 100,
            size: option.size || 2048,
            encoding: option.encoding,
        };
        this.size = option.size || 2048;
    }
    setEncode(encoding) {
        this.encoding = encoding;
        this.option.encoding = encoding;
    }
    async open() {
        try {
            if (!this.option.path) {
                return Promise.reject("Serialport path is empty");
            }
            if (!this.option.baudRate) {
                return Promise.reject("Serialport baudRate is empty");
            }
            if (this.isOpen) {
                return Promise.reject("Serialport is already open");
            }
            await invoke("plugin:serial|open", {
                path: this.option.path,
                baudRate: this.option.baudRate,
                dataBits: this.option.dataBits,
                flowControl: this.option.flowControl,
                parity: this.option.parity,
                stopBits: this.option.stopBits,
                timeout: this.option.timeout,
            });
            this.isOpen = true;
        }
        catch (error) {
            throw Promise.reject(error);
        }
    }
    async closeAll() {
        try {
            await invoke("plugin:serial|close_all");
        }
        catch (error) {
            throw Promise.reject(error);
        }
    }
    async cancelListen() {
        try {
            if (this.unListen) {
                this.unListen();
                this.unListen = undefined;
            }
            return;
        }
        catch (error) {
            return Promise.reject('Failed to cancel serial monitoring: ' + error);
        }
    }
    async cancelRead() {
        try {
            return await invoke('plugin:serial|cancel_read', {
                path: this.option.path,
            });
        }
        catch (error) {
            return Promise.reject(error);
        }
    }
    async listen(fn) {
        try {
            await this.cancelListen();
            let sub_path = this.option.path?.toString().replaceAll(".", "-").replaceAll("/", "-");
            let readEvent = `plugin-serial-read-${sub_path}`;
            console.log('listen event: ' + readEvent);
            this.unListen = await listen(readEvent, ({ payload }) => {
                try {
                    if (this.encoding !== 'none') {
                        const decoder = new TextDecoder(this.encoding);
                        const data = decoder.decode(new Uint8Array(payload.data));
                        fn(data);
                    }
                    else {
                        fn(new Uint8Array(payload.data));
                    }
                }
                catch (error) {
                    console.error(error);
                }
            });
            return;
        }
        catch (error) {
            return Promise.reject('Failed to monitor serial port data: ' + error);
        }
    }
    async startListening() {
        try {
            await invoke('plugin:serial|start_listening', {
                path: this.option.path,
                size: this.option.size,
                timeout: this.option.timeout,
            });
        }
        catch (error) {
            return Promise.reject(error);
        }
    }
    async stopListening() {
        try {
            await invoke('plugin:serial|stop_listening', {
                path: this.option.path,
            });
        }
        catch (error) {
            return Promise.reject(error);
        }
    }
    async forceClose(path) {
        return await invoke('plugin:serial|force_close', { path });
    }
    async close() {
        try {
            if (!this.isOpen) {
                return;
            }
            await this.cancelRead();
            let res = undefined;
            res = await invoke('plugin:serial|close', {
                path: this.option.path,
            });
            await this.cancelListen();
            this.isOpen = false;
            return res;
        }
        catch (error) {
            return Promise.reject(error);
        }
    }
    async read(options) {
        try {
            return await invoke('plugin:serial|read', {
                path: this.option.path,
                timeout: options?.timeout || this.option.timeout,
                size: options?.size || this.size,
            });
        }
        catch (error) {
            return Promise.reject(error);
        }
    }
    async write(value) {
        try {
            if (!this.isOpen) {
                return Promise.reject(`serial port ${this.option.path} not opened!`);
            }
            return await invoke('plugin:serial|write', {
                value,
                path: this.option.path,
            });
        }
        catch (error) {
            return Promise.reject(error);
        }
    }
    async writeBinary(value) {
        try {
            if (!this.isOpen) {
                return Promise.reject(`serial port ${this.option.path} not opened!`);
            }
            if (value instanceof Uint8Array || value instanceof Array) {
                return await invoke('plugin:serial|write_binary', {
                    value: Array.from(value),
                    path: this.option.path,
                });
            }
            else {
                return Promise.reject('value Argument type error! Expected type: string, Uint8Array, number[]');
            }
        }
        catch (error) {
            return Promise.reject(error);
        }
    }
    async setDataBits(value) {
        try {
            return await invoke('plugin:serial|set_data_bits', {
                path: this.option.path,
                dataBits: value
            });
        }
        catch (error) {
            return Promise.reject(error);
        }
    }
    async setBaudRate(value) {
        try {
            return await invoke('plugin:serial|set_baud_rate', {
                path: this.option.path,
                baudRate: value
            });
        }
        catch (error) {
            return Promise.reject(error);
        }
    }
    async setParity(value) {
        try {
            return await invoke('plugin:serial|set_parity', {
                path: this.option.path,
                parity: value
            });
        }
        catch (error) {
            return Promise.reject(error);
        }
    }
    async setStopBits(value) {
        try {
            return await invoke('plugin:serial|set_stop_bits', {
                path: this.option.path,
                stopBits: value
            });
        }
        catch (error) {
            return Promise.reject(error);
        }
    }
    async setRequestToSend(value) {
        try {
            return await invoke('plugin:serial|write_request_to_send', {
                path: this.option.path,
                level: value
            });
        }
        catch (error) {
            return Promise.reject(error);
        }
    }
    /**
     * @description Sets the DTR (Data Terminal Ready) control signal
     * @param {boolean} value The signal level to set
     * @returns {Promise<void>} A promise that resolves when DTR is set
     */
    async setDataTerminalReady(value) {
        try {
            return await invoke('plugin:serial|write_data_terminal_ready', {
                path: this.option.path,
                level: value
            });
        }
        catch (error) {
            return Promise.reject(error);
        }
    }
    /**
     * @description Reads the CTS (Clear To Send) control signal state
     * @returns {Promise<boolean>} A promise that resolves to the CTS state
     */
    async readClearToSend() {
        try {
            return await invoke('plugin:serial|read_clear_to_send', {
                path: this.option.path
            });
        }
        catch (error) {
            return Promise.reject(error);
        }
    }
    /**
     * @description Reads the DSR (Data Set Ready) control signal state
     * @returns {Promise<boolean>} A promise that resolves to the DSR state
     */
    async readDataSetReady() {
        try {
            return await invoke('plugin:serial|read_data_set_ready', {
                path: this.option.path
            });
        }
        catch (error) {
            return Promise.reject(error);
        }
    }
    /**
     * @description Reads the RI (Ring Indicator) control signal state
     * @returns {Promise<boolean>} A promise that resolves to the RI state
     */
    async readRingIndicator() {
        try {
            return await invoke('plugin:serial|read_ring_indicator', {
                path: this.option.path
            });
        }
        catch (error) {
            return Promise.reject(error);
        }
    }
    /**
     * @description Reads the CD (Carrier Detect) control signal state
     * @returns {Promise<boolean>} A promise that resolves to the CD state
     */
    async readCarrierDetect() {
        try {
            return await invoke('plugin:serial|read_carrier_detect', {
                path: this.option.path
            });
        }
        catch (error) {
            return Promise.reject(error);
        }
    }
    /**
     * @description Gets the number of bytes available to read
     * @returns {Promise<number>} A promise that resolves to the number of bytes
     */
    async bytesToRead() {
        try {
            return await invoke('plugin:serial|bytes_to_read', {
                path: this.option.path
            });
        }
        catch (error) {
            return Promise.reject(error);
        }
    }
    /**
     * @description Gets the number of bytes waiting to be written
     * @returns {Promise<number>} A promise that resolves to the number of bytes
     */
    async bytesToWrite() {
        try {
            return await invoke('plugin:serial|bytes_to_write', {
                path: this.option.path
            });
        }
        catch (error) {
            return Promise.reject(error);
        }
    }
    /**
     * @description Clears the specified buffer
     * @param {ClearBuffer} buffer The buffer to clear
     * @returns {Promise<void>} A promise that resolves when the buffer is cleared
     */
    async clearBuffer(buffer) {
        try {
            return await invoke('plugin:serial|clear_buffer', {
                path: this.option.path,
                bufferToClear: buffer
            });
        }
        catch (error) {
            return Promise.reject(error);
        }
    }
    /**
     * @description Starts transmitting a break signal
     * @returns {Promise<void>} A promise that resolves when break signal starts
     */
    async setBreak() {
        try {
            return await invoke('plugin:serial|set_break', {
                path: this.option.path
            });
        }
        catch (error) {
            return Promise.reject(error);
        }
    }
    /**
     * @description Stops transmitting a break signal
     * @returns {Promise<void>} A promise that resolves when break signal stops
     */
    async clearBreak() {
        try {
            return await invoke('plugin:serial|clear_break', {
                path: this.option.path
            });
        }
        catch (error) {
            return Promise.reject(error);
        }
    }
}

export { ClearBuffer, DataBits, FlowControl, Parity, SerialPort, StopBits, available_ports };
